#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>


#include"../common/log.hpp"
#include"../common/util.hpp"

namespace ns_run{
    using namespace ns_log;
    using namespace ns_util;
    class runner{
        public:
        runner()
        {}
        ~runner()
        {}
        public:
        //对资源进行控制 cpu使用时间 占用空间大小(KB)
        static void SerProcLimit(int cpu_limit,int mem_limit)
        {
            //设置CPU时长
            rlimit cpu_rlimit;
            cpu_rlimit.rlim_max = RLIM_INFINITY;// 硬限制，软限制不能超过硬限制
            cpu_rlimit.rlim_cur = cpu_limit;// 软限制，进程达到此限制时会收到信号
            setrlimit(RLIMIT_CPU,&cpu_rlimit);
            //设置内存大小
            rlimit mem_rlimit;
            mem_rlimit.rlim_max = RLIM_INFINITY;
            mem_rlimit.rlim_cur = mem_limit * 1024;//转换为KB
            setrlimit(RLIMIT_AS,&mem_rlimit);
        }
        
        static int run(const std::string &file_name,int cpu_limit,int mem_limit)
        {
            /*********************************************
            * 程序运行：
            * 1. 代码跑完，结果正确 = 0
            * 2. 代码跑完，结果不正确 > 0
            * 3. 代码没跑完，异常了 < 0
            * Run只考虑：是否正确运行完毕
            * 
            * 我们必须知道可执行程序是谁？
            * 一个程序在默认启动的时候
            * 标准输入: 不处理
            * 标准输出: 程序运行完成，输出结果是什么
            * 标准错误: 运行时错误信息
            * *******************************************/
            std::string _execute = PathUtil::src(file_name);
            std::string _stdin = PathUtil::stdin(file_name);
            std::string _stdout = PathUtil::stdout(file_name);
            std::string _stderr = PathUtil::stderr(file_name);
            //创建文件
            umask(0);
            int _stdin_fd = open(_stdin.c_str(),O_CREAT|O_WRONLY,0644);
            int _stdout_fd = open(_stdout.c_str(),O_CREAT|O_WRONLY,0644);
            int _stderr_fd = open(_stderr.c_str(),O_CREAT|O_WRONLY,0644);
            if(_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
            {
                LOG(ERROR)<<"打开标准文件失败!"<<std::endl;
                return -1;//代表打开文件失败
            }

            pid_t pid = fork();
            if(pid < 0)
            {
                LOG(ERROR)<<"创建子进程失败!"<<std::endl;
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2;//代表创建子进程失败
            }
            else if(pid == 0)
            {
                SerProcLimit(cpu_limit,mem_limit);
                //重定向
                dup2(_stderr_fd,0);
                dup2(_stdout_fd,1);
                dup2(_stderr_fd,2);
                execl(PathUtil::exe(file_name).c_str(),PathUtil::exe(file_name).c_str(),nullptr);
                exit(1);
            }
            else if(pid > 0)
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status = 0;
                waitpid(pid,&status,0);
                // 程序运行异常，一定是因为因为收到了信号！
                LOG(INFO) << "运行完毕, info: " << (status & 0x7F) << "\n";
                return status & 0x7f;
            }
            return 0;
        }
    };
}